#include "Renderer.h"
#include <Gwen/Texture.h>
#include <SDL_opengl.h>

PF::Renderer::Renderer()
{
	FT_Error kError = FT_Init_FreeType(&m_kLibrary);
	if(kError)
	{
		//...
	}
}

PF::Renderer::~Renderer()
{
	FT_Done_FreeType(m_kLibrary);
}

void PF::Renderer::LoadTexture(Gwen::Texture *a_pTexture)
{
	std::map<std::wstring, Gwen::Texture>::iterator it;

	std::wstring wszName = a_pTexture->name.GetUnicode();
	it = m_kTextureMap.find(wszName);
	if(it == m_kTextureMap.end())
	{
		Gwen::Renderer::OpenGL::LoadTexture(a_pTexture);
		m_kTextureMap[wszName] = *a_pTexture;
	}
	else
	{
		memcpy(a_pTexture, &it->second, sizeof(Gwen::Texture));
	}
}

void PF::Renderer::LoadFont(Gwen::Font* a_pFont)
{
	if(a_pFont->facename == L"")
	{
		return;
	}

	FT_Face kFace;

	FT_Error kError = FT_New_Face(m_kLibrary,
		Gwen::Utility::UnicodeToString(a_pFont->facename).c_str(),
		0,
		&kFace);

	if(kError == FT_Err_Unknown_File_Format)
	{
		//... the font file could be opened and read, but it appears
		//... that its font format is unsupported
		return;
	}
	else if(kError)
	{
		//... another error code means that the font file could not
		//... be opened or read, or simply that it is broken...
		return;
	}

	kError = FT_Set_Char_Size( 
		kFace,					/* handle to face object */
		a_pFont->size * 64,		/* char_width in 1/64th of points */
		a_pFont->size * 64,		/* char_height in 1/64th of points */
		96,						/* horizontal device resolution */
		96);					/* vertical device resolution */

	std::map<FT_ULong, PF::FontData> *m_pFontMap = new std::map<FT_ULong, PF::FontData>;
	a_pFont->data = (void*)m_pFontMap;

	FT_ULong  ulCharCode;
	FT_UInt   uiGIndex;

	ulCharCode = FT_Get_First_Char(kFace, &uiGIndex);
	while(ulCharCode != 0)
	{
		FT_Load_Glyph(
			kFace,									/* handle to face object */
			FT_Get_Char_Index(kFace, ulCharCode),	/* glyph index */
			FT_LOAD_DEFAULT);						/* load flags */

		FT_Render_Glyph(kFace->glyph,	/* glyph slot */
			FT_RENDER_MODE_NORMAL);		/* render mode */

		FT_GlyphSlot kGlyphSlot = kFace->glyph;
		FT_Bitmap &kBitmap = kGlyphSlot->bitmap;

		PF::FontData kFontData;
		kFontData.m_iLeft = kGlyphSlot->bitmap_left;
		kFontData.m_iTop = kGlyphSlot->bitmap_top;
		kFontData.m_iWidth = kBitmap.width;
		kFontData.m_iHeight = kBitmap.rows;
		kFontData.m_vAdvance = kGlyphSlot->advance;
		kFontData.m_pTexture = new Gwen::Texture;

		Gwen::Texture *pTexture = kFontData.m_pTexture;
		GLuint *pglTexture = new GLuint;
		pTexture->data = pglTexture;
		pTexture->width = 1;
		pTexture->height = 1;

		while(pTexture->width < kBitmap.width) pTexture->width <<= 1;
		while(pTexture->height < kBitmap.rows) pTexture->height <<= 1;

		GLubyte* pPixels = new GLubyte[2 * pTexture->width * pTexture->height];
		for(int y = 0; y < pTexture->height; ++y)
		{
			for(int x = 0; x < pTexture->width; ++x)
			{
				pPixels[2 * (x + y * pTexture->width)] = pPixels[2 * (x + y * pTexture->width) + 1] = 
					(x >= kBitmap.width || y >= kBitmap.rows) ?
					0 : kBitmap.buffer[x + kBitmap.width * y];
			}
		}

		glGenTextures(1, pglTexture);
		glBindTexture(GL_TEXTURE_2D, *pglTexture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture->width, pTexture->height, 0,
			GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pPixels);

		delete [] pPixels;
		m_pFontMap->insert(std::pair<FT_ULong, FontData>(ulCharCode, kFontData));

		ulCharCode = FT_Get_Next_Char(kFace, ulCharCode, &uiGIndex);
	}

	FT_Done_Face(kFace);
}

void PF::Renderer::FreeFont(Gwen::Font* a_pFont)
{
	if(a_pFont->data == NULL)
	{
		return;
	}

	std::map<FT_ULong, PF::FontData> *pFontMap;
	std::map<FT_ULong, PF::FontData>::iterator it;

	pFontMap = (std::map<FT_ULong, PF::FontData> *) a_pFont->data;
	for(it = pFontMap->begin(); it != pFontMap->end(); it++)
	{
		PF::FontData kFontData = it->second;
		FreeTexture(kFontData.m_pTexture);
	}

	delete pFontMap;
}

void PF::Renderer::RenderText(Gwen::Font* a_pFont, Gwen::Point a_kPos, const Gwen::UnicodeString& a_szText)
{
	if(a_pFont->data == NULL)
	{
		return;
	}

	std::map<FT_ULong, PF::FontData> *pFontMap;
	pFontMap = (std::map<FT_ULong, PF::FontData> *) a_pFont->data;

	for(unsigned int i = 0; i < a_szText.length(); ++i)
	{
		std::map<FT_ULong, PF::FontData>::iterator it;
		it = pFontMap->find(a_szText[i]);

		PF::FontData kFontData = it->second;
		Gwen::Texture *pTexture = kFontData.m_pTexture;

		Gwen::Rect kTargetRect(a_kPos.x + kFontData.m_iLeft, a_kPos.y - kFontData.m_iTop + a_pFont->size, kFontData.m_iWidth, kFontData.m_iHeight);
		DrawTexturedRect(pTexture, kTargetRect, 0.0f, 0.0f, (float) kFontData.m_iWidth / pTexture->width, (float) kFontData.m_iHeight / pTexture->height);

		a_kPos.x += kFontData.m_vAdvance.x >> 6;
	}
}

Gwen::Point PF::Renderer::MeasureText(Gwen::Font *a_pFont, const Gwen::UnicodeString &a_szText)
{
	if(a_pFont->data == NULL)
	{
		return Gwen::Point(0, 0);
	}

	std::map<FT_ULong, PF::FontData> *pFontMap;
	pFontMap = (std::map<FT_ULong, PF::FontData> *) a_pFont->data;

	Gwen::Point kSize(0, 0);
	for(unsigned int i = 0; i < a_szText.length(); ++i)
	{
		std::map<FT_ULong, PF::FontData>::iterator it;
		it = pFontMap->find(a_szText[i]);

		PF::FontData kFontData = it->second;
		kSize.x += kFontData.m_vAdvance.x >> 6;

		int iSize = kFontData.m_iHeight - kFontData.m_iTop + a_pFont->size;
		if(iSize > kSize.y)
		{
			kSize.y = iSize;
		}
	}

	return kSize;
}
